package references

import (
	"jvm/nzpJvm/instructions/base"
	"jvm/nzpJvm/runtime_area"
	"jvm/nzpJvm/runtime_area/heap"
)

// 一次性创建多维数组的指令
type MULTI_A_NEW_ARRAY struct {
	// 定位到常量池中一个类的符号引用
	index uint16
	// 维度
	dimensions uint8
}

// 此指令的第一个操作数是一个对常量池的索引，指向一个ClassInfo结构
// 第二个操作数是维度，代表这是几维数组
// 后面跟着若干个操作数，分别代表每一个维度的长度，从操作数栈中依次弹出
func (self *MULTI_A_NEW_ARRAY) Execute(frame *runtime_area.Frame) {
	constantPool := frame.Method().Class().ConstantPool()
	classRef := constantPool.GetConstant(uint(self.index)).(*heap.ClassRef)
	// 此引用类型数组的组件类型
	componentType := classRef.ResolveClass()

	stack := frame.OperandStack()
	// 数组每一个维度的长度
	counts := popAndCheckCounts(stack,int(self.dimensions))

	// 创建数组对象
	arrayObject := newMultiDimensionsArray(counts,componentType);
	// 新的数组对象入栈
	stack.PushRef(arrayObject)
}

// 根据指定的组件类型，创建多维数组
func newMultiDimensionsArray(counts []int32, componentType *heap.Class) *heap.Object {
	// 当前维度的数组长度
	count := uint(counts[0])
	// 创建指定长度的数组
	array := componentType.NewArray(count)
	// 还没创建完所有子维度
	if len(counts) > 1 {
		refs := array.Refs()
		for i := range refs {
			// 递归创建此数组的子数组
			refs[i] = newMultiDimensionsArray(counts[1:],componentType.ComponentClass())
		}
	}
	return array
}

// 多维数组每一个维度的长度，从操作数栈中依次弹出
func popAndCheckCounts(stack *runtime_area.OprandStack, dimensions int) []int32 {
	// 会从操作数栈中弹出dimensions个数，代表dimensions个维度的多维数组每一个维度的长度
	// 例如 int[2][3][4],dimensions为3
	// 2、3、4依次从操作数栈中弹出
	counts := make([]int32, dimensions)
	for i := dimensions-1; i >= 0; i-- {
		counts[i] = stack.PopInt()
		if counts[i] <0 {
			panic("java.lang.NegativeArraysSizeException")
		}
	}
	return counts
}

func (self *MULTI_A_NEW_ARRAY)FetchOperands(reader *base.BytecodeReader)  {
	// 获取两个字节，并且组合成一个无符号整数作为操作数
	self.index = reader.ReadUint16()
	self.dimensions = reader.ReadUint8()
}
